package com.xiaoyu.base.net.proxy;

import com.xiaoyu.base.AppConfig;
import com.xiaoyu.net.DoRequestTask;
import com.xiaoyu.net.RequestProvider;
import com.xiaoyu.net.util.NetUtils;
import com.xiaoyu.base.BuildConfig;
import com.xiaoyu.base.data.UserData;
import com.xiaoyu.base.net.NetworkErrorProcessor;

import in.srain.cube.concurrent.AppThreads;
import in.srain.cube.concurrent.SimpleTask;
import in.srain.cube.request.FailData;
import in.srain.cube.request.IRequest;
import in.srain.cube.request.IRequestProxy;
import in.srain.cube.request.JsonData;
import in.srain.cube.util.CLog;
import in.srain.cube.util.Env;

import static com.xiaoyu.net.RequestProvider.doSyncRequest;
import static com.xiaoyu.net.config.Constant.QUERY_KEY_SIGN;
import static com.xiaoyu.net.config.Constant.TAG_NET;

public abstract class BaseRequestProxy<OriginDataType> implements IRequestProxy<OriginDataType> {

    protected void processExtData(JsonData extData) {
        if (extData.has("in_dev_mode")) {
            Env.setInDevMode(extData.optBoolean("in_dev_mode"));
        }
    }

    @Override
    public <T> T requestSync(final IRequest<T, OriginDataType> request) throws Throwable {
        final T data = doSyncRequest(request, RequestProvider.getInstance().buildRequest(request), this);
        if (request.isPostToMainThread()) {
            SimpleTask.post(new Runnable() {
                @Override
                public void run() {
                    postResult(data, request);
                }
            });
        } else {
            postResult(data, request);
        }
        return data;
    }

    private <T> T postResult(T data, final IRequest<T, OriginDataType> request) {
        if (data == null || request.getFailData() != null) {
            request.onRequestFail(null, request.getFailData());
        } else {
            request.onRequestSuccess(data);
        }
        return data;
    }

    /*
     * {@link RequestBase#send()} will trigger this call.
     * <p>
     * But we introduce {@link RequestQueue#enqueueRequest}, in {@link RequestJob#onRun}, we call {@link #requestSync} instead of this method
     *
     * @param request
     * @param <T>
     */
    @Override
    public <T> void sendRequest(IRequest<T, OriginDataType> request) {
        triggerError();
        if (BuildConfig.DEBUG) {
            CLog.d(TAG_NET, "sendRequest, request.isPostToMainThread: %s", request.isPostToMainThread());
        }
        if (request.isPostToMainThread()) {
            DoRequestTask<T, OriginDataType> doRequestTask = new DoRequestTask(request, RequestProvider.getInstance().buildRequest(request), this);
            AppThreads.runOnIOThread(doRequestTask);
        } else {
            AppThreads.runOnIOThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        requestSync(request);
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                    }
                }
            });
        }
    }

    private void triggerError() {
        throw new RuntimeException("Don't use send(), use enqueue instead.");
    }

    @Override
    public <T> void prepareRequest(IRequest<T, OriginDataType> request) {

        final String sign = NetUtils.buildSign(
                AppConfig.appKey(),
                request.getRequestData().getMethod(),
                request.getRequestData().mUrl,
                request.getRequestData().getQueryData(),
                request.getRequestData().getPostData(),
                UserData.getInstance().getTokenInfo().getSecretKey());

        request.getRequestData().addQueryData(QUERY_KEY_SIGN, sign);

        if (UserData.getInstance().hasLogin()) {
//            request.getRequestData().addHeader(HEADER_KEY_TOKEN, UserData.getInstance().getTokenInfo().getAccessToken());
//            if (!Env.isProd()) {
//                request.getRequestData().addQueryData(QUERY_KEY_TOKEN, UserData.getInstance().getTokenInfo().getAccessToken());
//            }
//            request.getRequestData().addQueryData(QUERY_KEY_TIMESTAMP, System.currentTimeMillis() / 1000);
        } else {
            CLog.w(TAG_NET, "prepareRequest user not login: uid: %s, token info: %s", UserData.getInstance().getUid(), UserData.getInstance().getTokenInfo());
        }
    }

    @Override
    public <T> void onRequestFail(IRequest<T, OriginDataType> request, Object requestTag, FailData failData) {
        NetworkErrorProcessor.getInstance().onRequestFail(request, requestTag, failData);
    }
}
